/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Compilador.Sintatico.ArvoreSintatica;

import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;

/**
 *
 * @author Rafa
 */
public class ArvoreSintatica implements Cloneable {

    private NoArvoreSintatica noRaiz;
    private NoArvoreSintatica noAtual;
    private ArrayList<NoArvoreSintatica> nosArvores;

    public ArvoreSintatica(NoArvoreSintatica noRaiz) {
        this.noRaiz = noRaiz;
        nosArvores = new ArrayList<NoArvoreSintatica>();
        nosArvores.add(noRaiz);
        noAtual = noRaiz;
    }

    public void insertNodes(ArrayList<NoArvoreSintatica> nosArvore) {
        Collections.reverse(nosArvore);
        System.out.println("No Pai: " + noAtual.getNoDescricao());

        for (int count = 0; count < nosArvore.size(); count++) {
            System.out.print("No Filho: " + nosArvore.get(count).getNoDescricao() + " ");

        }
        System.out.println();
        noAtual.setExpandiu(true);
        noAtual.setNoFilhos(nosArvore);
        if (nosArvore.size() > 0) {
            noAtual = nosArvore.get(0);
            nosArvores.addAll(nosArvore);
        }


    }
    // método que retira os não terminais que não geram algum terminal e não terminais que geram cadeia vazia

    public void reduzArvore() {
        ArrayList<NoArvoreSintatica> noRemovidos = new ArrayList<NoArvoreSintatica>();

        for (int count = 0; count < nosArvores.size(); count++) {
            NoArvoreSintatica no = nosArvores.get(count);
            if (no != noRaiz) {
                if (no.getNoFilhos().isEmpty() && !no.isTerminal()) {
                    if (no.getPai() != null) {
                        no.getPai().getNoFilhos().remove(no);
                    }
                    noRemovidos.add(no);
                } else if (!no.isTerminal() && checkFilhos(no.getNoFilhos())) {
                    for (int count2 = 0; count2 < no.getNoFilhos().size(); count2++) {
                        NoArvoreSintatica noFilho = no.getNoFilhos().get(count2);
                        noFilho.setPai(no.getPai());
                    }
                    int posicao = no.getPai().getNoFilhos().indexOf(no);
                    if (posicao != no.getPai().getNoFilhos().size() - 1) {
                        ArrayList<NoArvoreSintatica> subList1 = new ArrayList<NoArvoreSintatica>(no.getPai().getNoFilhos().subList(0, posicao + 1));
                        ArrayList<NoArvoreSintatica> subList2 = new ArrayList<NoArvoreSintatica>(no.getPai().getNoFilhos().subList(posicao + 1, no.getPai().getNoFilhos().size()));
                        subList1.addAll(no.getNoFilhos());
                        subList1.addAll(subList2);
                        no.getPai().setNoFilhos(subList1);

                    } else {
                        no.getPai().getNoFilhos().addAll(no.getNoFilhos());
                    }
                    no.getPai().getNoFilhos().remove(no);



                    /// no.getPai().getNoFilhos().add(noFilho);
                    noRemovidos.add(no);
                }
            }


        }
        nosArvores.removeAll(noRemovidos);

    }

    public void atualizaTypeNoAtual(TypeNo type) {
        noAtual.setTypeNo(type);
    }
    //

    private boolean checkFilhos(ArrayList<NoArvoreSintatica> nos) {
        for (int i = 0; i < nos.size(); i++) {
            NoArvoreSintatica noArvoreSintatica = nos.get(i);
            if (noArvoreSintatica.isTerminal()) {
                return false;
            }

        }
        return true;
    }

    //Busca o novo noAtual 
    public void atualizaNoAtual(String descricao) {
        NoArvoreSintatica no = noAtual.getPai();
        ArrayList<NoArvoreSintatica> nos;
        boolean percorrido = false;
        while (!percorrido) {
            if (no.getPai() == null) {
                percorrido = true;
            }
            nos = no.getNoFilhos();
            for (int count = 0; count < nos.size(); count++) {
                NoArvoreSintatica noArv = nos.get(count);
                if (noArv != noAtual) {
                    if (noArv.getNoDescricao().equals(descricao) && noArv.isExpandiu() == false) {
                        noAtual = noArv;
                        return;
                    }
                }
            }
            no = no.getPai();

        }

    }

    public void atualizaNomeNo(String descricao) {
        noAtual.setNoDescricao(descricao);

    }
    
    public void atualizaPosicaoNo(int[] posicao){
        noAtual.setLinha(posicao[0]);
        if(noAtual.getLinha() == -1){
            System.out.println("la");
        }
        noAtual.setColuna(posicao[1]);
        
    }

    //1 pré-ordem
    //2 pós-ordem
    public void imprimeArvore(int ordem) {
        if (ordem == 1) {


            try {

                FileWriter fileW;
                fileW = new FileWriter("arvoreSintatica.xml");
                Element arvoreSintatica = new Element("ArvoreSintatica");
                Document arvoreSintaticaXML = new Document(arvoreSintatica);
                imprimeArvorePreOrdem(noRaiz, arvoreSintatica);
                XMLOutputter xout = new XMLOutputter();
                xout.output(arvoreSintaticaXML, fileW);
                xout.setFormat(Format.getPrettyFormat());
                fileW.close();
            } catch (IOException ex) {
                Logger.getLogger(ArvoreSintatica.class.getName()).log(Level.SEVERE, null, ex);
            }

        } else if (ordem == 2) {

            try {

                FileWriter fileW;
                fileW = new FileWriter("arvoreSintatica.xml");
                Element arvoreSintatica = new Element("ArvoreSintatica");
                Document arvoreSintaticaXML = new Document(arvoreSintatica);
                imprimeArvorePosOrdem(noRaiz, arvoreSintatica);
                XMLOutputter xout = new XMLOutputter();
                xout.output(arvoreSintaticaXML, fileW);
                xout.setFormat(Format.getPrettyFormat());
                fileW.close();
            } catch (IOException ex) {
                Logger.getLogger(ArvoreSintatica.class.getName()).log(Level.SEVERE, null, ex);
            }

        }

    }

    private void imprimeArvorePreOrdem(NoArvoreSintatica no, Element elementRoot) {
        ArrayList<NoArvoreSintatica> nos = no.getNoFilhos();


        if (nos.size() > 0) {
            System.out.println(no.getNoDescricao() + " ");
            Element newElementXML = new Element(no.getNoDescricao());
            String text = "";
            for (int count = 0; count < nos.size(); count++) {
                if (nos.get(count).getTypeNo() == TypeNo.String) {
                    text += "\"" + nos.get(count).getNoDescricao() + "\"";
                } else {
                    text += nos.get(count).getNoDescricao();
                }
                if (count + 1 < nos.size()) {
                    text += " ";
                }
                System.out.println(no.getNoDescricao() + "->filho: " + nos.get(count).getNoDescricao());
            }

            newElementXML.setText(text);
            elementRoot.addContent(newElementXML);


            for (int count = 0; count < nos.size(); count++) {
                imprimeArvorePreOrdem(nos.get(count), elementRoot);
            }


        }



    }

    private void imprimeArvorePosOrdem(NoArvoreSintatica no, Element elementRoot) {
        ArrayList<NoArvoreSintatica> nos = no.getNoFilhos();


        if (nos.size() > 0) {
            System.out.println(no.getNoDescricao() + " ");
            Element newElementXML = new Element(no.getNoDescricao());
            String text = "";
            for (int count = nos.size() - 1; count >= 0; count--) {
                text += nos.get(count).getNoDescricao();
                if (count + 1 < nos.size()) {
                    text += " ";
                }
                System.out.println(no.getNoDescricao() + "->filho: " + nos.get(count).getNoDescricao());
            }

            newElementXML.setText(text);
            elementRoot.addContent(newElementXML);


            for (int count = nos.size() - 1; count >= 0; count--) {
                imprimeArvorePreOrdem(nos.get(count), elementRoot);
            }


        }
    }

    //Método recursivo que irá percorrer os nós e criar um clone pra cada um
    private ArrayList<NoArvoreSintatica> cloneNos(ArrayList<NoArvoreSintatica> nos, NoArvoreSintatica noPai, ArrayList<NoArvoreSintatica> nosGerados, ArvoreSintatica arvoreSintatica) {
        ArrayList<NoArvoreSintatica> nosNovos = new ArrayList<NoArvoreSintatica>();
        for (int count = 0; count < nos.size(); count++) {
            NoArvoreSintatica novoNo = new NoArvoreSintatica();
            if (nos.get(count) == noAtual) {
                arvoreSintatica.setNoAtual(novoNo);
            }
            novoNo.setTerminal(nos.get(count).isTerminal());
            novoNo.setExpandiu(nos.get(count).isExpandiu());
            novoNo.setNoDescricao(nos.get(count).getNoDescricao());
            novoNo.setNoFilhos(cloneNos(nos.get(count).getNoFilhos(), novoNo, nosGerados, arvoreSintatica));
            novoNo.setPai(noPai);
            novoNo.setTypeNo(nos.get(count).getTypeNo());
            novoNo.setColuna(nos.get(count).getColuna());
            novoNo.setLinha(nos.get(count).getLinha());
            nosNovos.add(novoNo);
            nosGerados.add(novoNo);
        }
        return nosNovos;
    }

    @Override
    public Object clone() {
        ArrayList<NoArvoreSintatica> nosNovos = new ArrayList<NoArvoreSintatica>();
        NoArvoreSintatica novoNo = new NoArvoreSintatica();
        novoNo.setExpandiu(noRaiz.isExpandiu());
        novoNo.setNoDescricao(noRaiz.getNoDescricao());
        novoNo.setTypeNo(noRaiz.getTypeNo());
        novoNo.setColuna(0);
        novoNo.setLinha(0);
        novoNo.setPai(null);
        ArvoreSintatica arvoreSintatica = new ArvoreSintatica(novoNo);
        arvoreSintatica.setNosArvores(nosNovos);
        nosNovos.add(novoNo);
        novoNo.setNoFilhos(cloneNos(noRaiz.getNoFilhos(), novoNo, nosNovos, arvoreSintatica));
        return arvoreSintatica;


    }

    public NoArvoreSintatica getNoAtual() {
        return noAtual;
    }

    public void setNoAtual(NoArvoreSintatica noAtual) {
        this.noAtual = noAtual;
    }

    public NoArvoreSintatica getNoRaiz() {
        return noRaiz;
    }

    public void setNoRaiz(NoArvoreSintatica noRaiz) {
        this.noRaiz = noRaiz;
    }

    public ArrayList<NoArvoreSintatica> getNosArvores() {
        return nosArvores;
    }

    public void setNosArvores(ArrayList<NoArvoreSintatica> nosArvores) {
        this.nosArvores = nosArvores;
    }
}
